import os
import pandas as pd
os.environ['JAX_PLATFORMS'] = 'cpu'

from apdsde import apdsde
from fade import fade_optimizer
from sade import sade
from env import optimize_with_rl_de
from test_pyade import jso_optimizer, ilshade_optimizer, jade_optimizer


ea_map = {
    'apdsde': apdsde,
    'fade': fade_optimizer,
    'sade': sade,
    'rlpde': optimize_with_rl_de,
    'jso': jso_optimizer,
    'ilshade': ilshade_optimizer,
    'jade': jade_optimizer
}


# 测试一次所有算法，所有函数
def run_single(func_range, dim, max_evals, pop_size):
    # 确保数据目录存在
    os.makedirs(f'data/{dim}/single', exist_ok=True)
    
    for func_id in range(func_range[0], func_range[1]):
        print(f"Processing function {func_id}...")
        
        # 存储所有算法的历史适应度值
        all_histories = {}
        max_history_length = 0
        
        # 运行每个算法并收集历史记录
        for key in ea_map:
            func = ea_map[key]
            best_fitness, solve, history = func(func_id, dim, max_evals, pop_size)
            all_histories[key] = history
            max_history_length = max(max_history_length, len(history))
            print(f'  {key}: best_fitness = {best_fitness}, history length = {len(history)}')
        
        # 重新排序键，确保'rlpde'在第一列
        ordered_keys = ['rlpde'] + [k for k in ea_map.keys() if k != 'rlpde']
        
        # 创建数据框，对齐历史记录长度
        df_data = {}
        for key in ordered_keys:
            history = all_histories[key]
            df_data[key] = history
        
        # 创建数据框并保存为CSV文件
        df = pd.DataFrame(df_data)
        csv_path = f'data/{dim}//singel/F{func_id}.csv'
        df.to_csv(csv_path, index=False)
        print(f"  Saved results to {csv_path}")
import numpy as np

def run_multiple(func_range, dim, max_evals, pop_size, n_runs):
    """
    运行多次实验并计算统计结果，保证rlpde始终在第一列
    
    Args:
        func_range: 测试函数ID范围，例如(31, 43)表示测试从31到42的函数
        dim: 问题维度
        max_evals: 最大评估次数
        pop_size: 种群大小
        n_runs: 每个算法每个函数重复运行的次数
    """
    # 确保数据目录存在
    os.makedirs(f'data/{dim}', exist_ok=True)
    
    # 准备数据结构
    results = {}
    
    # 对每个函数进行测试
    for func_id in range(func_range[0], func_range[1]):
        func_name = f'F{func_id}'
        print(f"处理函数 {func_name}...")
        results[func_name] = {}
        
        # 对每个算法进行测试
        for key in ea_map:
            print(f"  运行 {key}...")
            func = ea_map[key]
            
            # 运行n次
            fitness_values = []
            for run in range(n_runs):
                best_fitness, solve, history = func(func_id, dim, max_evals, pop_size)
                fitness_values.append(best_fitness)
                print(f"    运行 {run+1}/{n_runs}: {best_fitness}")
            
            # 存储该算法的结果
            results[func_name][key] = fitness_values
    
    # 重新排序键，确保'rlpde'在第一列
    ordered_keys = ['rlpde'] + [k for k in ea_map.keys() if k != 'rlpde']
    
    # 准备DataFrame数据
    df_data = []
    
    for func_name in sorted(results.keys()):
        # 为每个算法计算统计数据
        algorithm_stats = {}
        for key in ea_map.keys():
            values = results[func_name][key]
            algorithm_stats[key] = {
                'mean': np.mean(values),
                'std': np.std(values),
                'median': np.median(values),
                'best': np.min(values),
                'worst': np.max(values)
            }
        
        # 根据均值计算排名（值越小排名越靠前）
        means = [(key, algorithm_stats[key]['mean']) for key in ea_map.keys()]
        means.sort(key=lambda x: x[1])  # 按均值升序排序
        rankings = {alg: rank+1 for rank, (alg, _) in enumerate(means)}
        
        # 添加行到DataFrame
        metrics = ['mean', 'rank', 'std', 'median', 'best', 'worst']
        for i, metric in enumerate(metrics):
            row = {'Function': func_name if i == 0 else '', 'Metric': metric}
            
            # 确保rlpde始终是第一列，但保留其实际排名
            for key in ordered_keys:
                if metric == 'rank':
                    row[key] = rankings[key]
                else:
                    row[key] = algorithm_stats[key][metric]
            
            df_data.append(row)
    
    # 创建DataFrame
    df = pd.DataFrame(df_data)
    
    # 格式化科学计数法
    for col in ordered_keys:
        for metric in ['mean', 'std', 'median', 'best', 'worst']:
            mask = df['Metric'] == metric
            df.loc[mask, col] = df.loc[mask, col].apply(lambda x: f"{x:.2e}")
    
    # 保存到CSV
    csv_file = f'data/{dim}/stats_summary.csv'
    df.to_csv(csv_file, index=False)
    print(f"统计结果已保存到 {csv_file}")
    
    return results

import matplotlib.pyplot as plt
import os
import numpy as np

def plot_convergence(dim, func_range):
    """
    根据run_single函数生成的数据绘制收敛曲线图
    
    Args:
        dim: 问题维度
        func_range: 函数ID范围，例如(31, 43)表示从F31到F42
    """
    # 确保图片目录存在
    os.makedirs(f'graph/{dim}', exist_ok=True)
    
    # 定义各算法的颜色
    colors = {
        'rlpde': 'red',
        'fade': 'blue',
        'sade': 'green',
        'apdsde': 'purple',
        'jade': 'orange',
        'ilshade': 'brown',
        'jso': 'black',
        'lshade': 'pink'
    }
    
    # 处理每个函数
    for func_id in range(func_range[0], func_range[1]):
        print(f"绘制函数F{func_id}的收敛曲线...")
        
        # 读取CSV数据
        csv_path = f'data/{dim}/single/F{func_id}.csv'
        if not os.path.exists(csv_path):
            print(f"  未找到数据文件: {csv_path}")
            continue
            
        df = pd.read_csv(csv_path)
        
        # 绘制图形
        plt.figure(figsize=(10, 6))
        
        # 获取数据长度
        max_length = len(df)
        
        # 为每个算法绘制曲线
        for alg in df.columns:
            if alg == "rlpde": # RLPDE 保持原有的粗线和圆点标记
                plt.plot(
                    range(1, max_length + 1),
                    df[alg],
                    label=alg,
                    color=colors.get(alg, 'red'),
                    linewidth=4,
                    marker='o',
                    markersize=10,
                    markevery=max(1, max_length//100)  # 每5%的点标记一次
                )
            elif alg == "fade": # FADE: 实线, 方块标记
                plt.plot(
                    range(1, max_length + 1),
                    df[alg],
                    label=alg,
                    color=colors.get(alg, 'blue'),
                    linewidth=2,
                    linestyle='-', # 实线
                    marker='s',    # 方块标记
                    markersize=5,
                    markevery=max(1, max_length//100)
                )
            elif alg == "sade": # SADE: 虚线, 三角形标记
                plt.plot(
                    range(1, max_length + 1),
                    df[alg],
                    label=alg,
                    color=colors.get(alg, 'green'),
                    linewidth=2,
                    linestyle='--', # 虚线
                    marker='^',     # 三角形标记
                    markersize=5,
                    markevery=max(1, max_length//100)
                )
            elif alg == "apdsde": # APDSDE: 点线, 菱形标记
                plt.plot(
                    range(1, max_length + 1),
                    df[alg],
                    label=alg,
                    color=colors.get(alg, 'purple'),
                    linewidth=2,
                    linestyle=':',  # 点线
                    marker='D',     # 菱形标记
                    markersize=5,
                    markevery=max(1, max_length//100)
                )
            elif alg == "jade": # JADE: 点划线, x 标记
                plt.plot(
                    range(1, max_length + 1),
                    df[alg],
                    label=alg,
                    color=colors.get(alg, 'orange'),
                    linewidth=2,
                    linestyle='-.', # 点划线
                    marker='x',     # x 标记
                    markersize=5,
                    markevery=max(1, max_length//100)
                )
            elif alg == "ilshade": # ILSHADE: 实线, 星号标记
                plt.plot(
                    range(1, max_length + 1),
                    df[alg],
                    label=alg,
                    color=colors.get(alg, 'brown'),
                    linewidth=2,
                    linestyle='-',  # 实线
                    marker='*',     # 星号标记
                    markersize=5,
                    markevery=max(1, max_length//100)
                )
            elif alg == "jso": # JSO: 点划线, 圆点标记
                plt.plot(
                    range(1, max_length + 1),
                    df[alg],
                    label=alg,
                    color=colors.get(alg, 'black'),
                    linewidth=2,
                    linestyle='-.', # 点划线
                    marker='.',     # 圆点标记
                    markersize=5,
                    markevery=max(1, max_length//100)
                )
            else: # 默认情况
                plt.plot(
                    range(1, max_length + 1),
                    df[alg],
                    label=alg,
                    linewidth=2
                )
        
        # 设置图表样式
        plt.xlabel("评估次数", fontsize=12)
        plt.ylabel("最佳适应度", fontsize=12)
        plt.yscale("log")  # 对数刻度
        plt.title(f"函数F{func_id}的收敛曲线", fontsize=14)
        plt.grid(True, linestyle="--", alpha=0.7)
        plt.legend(fontsize=10)
        
        # 保存图片
        plt.tight_layout()
        fig_path = f'graph/{dim}/F{func_id}.png'
        plt.savefig(fig_path, dpi=300)
        plt.close()
        print(f"  图片已保存到: {fig_path}")


# 比较算法性能
import pandas as pd
import numpy as np
import scipy.stats as stats

def analyze_ranks(dim,csv_path):
    # Read the CSV file
     
    df = pd.read_csv(csv_path)
    
    # Extract rank data
    rank_data = df[df['Metric'] == 'rank'].copy()
    
    # Get algorithm names (excluding Function and Metric columns)
    algorithms = [col for col in rank_data.columns if col not in ['Function', 'Metric']]
    
    # Extract ranks for each function
    ranks = rank_data[algorithms].to_numpy().astype(float)
    
    # Calculate average ranks
    avg_ranks = np.mean(ranks, axis=0)
    
    # Create a dictionary mapping algorithms to their average ranks
    avg_rank_dict = {alg: rank for alg, rank in zip(algorithms, avg_ranks)}
    
    # Sort algorithms by average rank (ascending)
    sorted_algorithms = sorted(avg_rank_dict.items(), key=lambda x: x[1])
    
    # Perform Friedman test
    friedman_stat, p_value = stats.friedmanchisquare(*[rank_data[alg] for alg in algorithms])
    
    # Create output text
    output = f"Friedman Test Results for {dim}D:\n"
    output += f"F-statistic: {friedman_stat:.4f}\n"
    output += f"p-value: {p_value:.6f}\n\n"
    output += "Average Rankings:\n"
    
    for alg, rank in sorted_algorithms:
        output += f"{alg}: {rank:.2f}\n"
    
    # Write results to file
    output_path = f'data/{dim}/friedman_test_results.txt'
    with open(output_path, 'w') as f:
        f.write(output)
    
    print(f"Friedman test results saved to {output_path}")
    return friedman_stat, p_value, sorted_algorithms

 

if __name__ == '__main__':
    
    ## 测试单个函数
    # func_id = 32 
    # dim = 10
    # max_evals = 10000 * dim 
    # pop_size = dim * 5
    # for key in ea_map:
    #     func = ea_map[key]
    #     best_fitness, slove, history = func(func_id, dim, max_evals, pop_size)
    #     print(f'key: {key}, func_id: {func_id}, best_fitness: {best_fitness},历史长度: {len(history)}')

    # 测试所有函数
    func_range = (31, 43)
    
    dim = 20
    max_evals = 10000 * dim
    pop_size = dim * 5
    
    #  绘图
    # plot_convergence(dim=dim, func_range=func_range)
    
    # 运行单次实验
    # run_single(func_range, dim, max_evals, pop_size)
    
    # # 运行多次实验
    # for dim in [10,20]:   
    #     n_runs = 30
    #     results = run_multiple(func_range, dim, max_evals, pop_size, n_runs)
        
   # 分析算法性能
    for dim in [10,20]:
        csv_path = f'data/{dim}/stats_summary.csv'
        analyze_ranks(dim, csv_path)
    
    
    
  
    
    